S3にログを出力するAWS WAFの構築
ALBでAWS WAFv2(以下WAF)を利用する環境を構築します。以下のような構成です。
S3にWAFのログを出力する際は、Kinesis Data Firehoseが必要になります。ここでは、Kinesis Data Firehoseで配信(S3への出力)エラーが発生した際に、CloudWatch Logsへロギングされる構成にしました。
ALBなどVPCリソースが構築済みであることを前提に、赤枠の箇所を中心に構築(説明)していきたいと思います。
前提
- VPCリソースが構築済みであること
- 前提となる環境をCFnテンプレートにしました
- Gist/ALB-PublicWeb.yml
WAFと周辺リソース構築
赤枠の箇所を構築するCFnテンプレートを作成しました。前提の環境が構築されていれば、利用できるようになっています。
CFnテンプレートを用意しましたので、マネジメントコンソールからの構築については割愛しています。 構築自体は上記CFnテンプレートにてスタックを作成すれば済みますので、ここからは作成されるリソースについて説明していきたいと思います。
S3
WAFログの出力先となるS3バケットです。Kinesis Data Firehoseを作成するリージョンと、同一リージョンに作成する必要があります。ここでは、S3のライフサイクルールにてログの保存期限を設定しました。
Kinesis Data Firehose
配信ストリーム名
WAFのログ出力に利用する際は制約があり、配信ストリーム名はaws-waf-logs-
ではじめる必要があります。
圧縮設定
S3保存量、ログの確認(Athena、S3 Select..)を考慮し、コストの抑制のためGZIP圧縮を有効にしました。
エラーログ設定
配信(S3への出力)エラーを考慮して、Kinesis Data FirehoseのエラーログをCloudWatch Logsに出力する設定にしています。配信エラーの詳細については、以下を確認ください。
プレフィックス設定
Athenaで解析がしやすいように、カスタムパーティションを設定しました。
サービスロール
今回の構成では、S3とCloudWatch Logsに対する権限が必要になります。
設定については以上となります。
東京リージョンのKinesis Data Firehoseでは、1,000 レコード/秒、1,000 トランザクション/秒、1 MiB/秒の上限が設定されています。これに抵触する可能性がある場合は、上限緩和申請を実施してください。
CloudWatch Logs
Kinesis Data Firehoseにてエラーログの記録を有効にする場合かつ、マネジメントコンソール以外で配信ストリームを作成する場合は、ロググループ、ログストリームを事前に作成する必要があります。
ここでは、ログデータを90日で失効するようにしました。今回は設定していませんが、該当のロググループにメトリクスフィルターを設定して、エラー発生時にアラートを通知させることも可能です。
WAF
今回はWeb ACLにルールを設定していませんので、実際に利用する際は何らかのルールを追加してください。Kinesis Data Firehoseなど上記に記載したリソース作成後、WAFのコンソールよりロギングを設定してください。(CFnにてWAFのログ設定が見当たりませんでした。)
なお、特定の項目をログから除外することも可能ですので、Cookieなど除外したい項目がある場合は、以下を参考にしてください。
ログに出力される項目など、詳細については以下を確認ください。
確認
今回は、WAFにルールを追加していませんので、WAFを利用しているALBにアクセスし、WAFのログが出力されることを確認したいと思います。
$ curl http://ALBのDNS名 <html><body>Hello world</body></html>
WAFのログが出力されていることを確認します。
$ BUCKET_NAME=バケット名 $ aws s3 ls s3://${BUCKET_NAME} --recursive 2020-06-06 19:09:28 2599 year=2020/month=06/day=06/hour=10/aws-waf-logs-kof-dev-delivery-stream-2-2020-06-06-10-08-25-1e9ba238-37df-4d72-ba98-12ed648b7f4c.gz 2020-06-06 19:10:29 2588 year=2020/month=06/day=06/hour=10/aws-waf-logs-kof-dev-delivery-stream-2-2020-06-06-10-09-26-ea76bd9d-0981-4904-b9d1-1e3670e46346.gz
ログの内容を確認しました。(一部マスクしています。)
$ aws s3 cp s3://${BUCKET_NAME}/year=2020/month=06/day=06/hour=10/aws-waf-logs-kof-dev-delivery-stream-2-2020-06-06-10-08-25-1e9ba238-37df-4d72-ba98-12ed648b7f4c.gz ./log.gz download: s3://XXXXXXXXXXXX/year=2020/month=06/day=06/hour=10/aws-waf-logs-kof-dev-delivery-stream-2-2020-06-06-10-08-25-1e9ba238-37df-4d72-ba98-12ed648b7f4c.gz to ./log.gz $ gzcat log.gz {"timestamp":1591438087848,"formatVersion":1,"webaclId":"arn:aws:wafv2:ap-northeast-1:XXXXXXXXXXXX:regional/webacl/kof-dev-web-acl/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","terminatingRuleId":"Default_Action","terminatingRuleType":"REGULAR","action":"ALLOW","terminatingRuleMatchDetails":[],"httpSourceName":"ALB","httpSourceId":"XXXXXXXXXXXX-app/test-dev-alb/XXXXXXXXXXXXXXXX","ruleGroupList":[],"rateBasedRuleList":[],"nonTerminatingMatchingRules":[],"httpRequest":{"clientIp":"XX.XX.XX.XX","country":"CN","headers":[{"name":"Host","value":"XX.XX.XX.XX"},{"name":"Content-Length","value":"25"},{"name":"Content-Type","value":"application/x-www-form-urlencoded"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0"},{"name":"Cache-Control","value":"no-cache"}],"uri":"/aotu7.php","args":"","httpVersion":"HTTP/1.1","httpMethod":"POST","requestId":null}} {"timestamp":1591438094790,"formatVersion":1,"webaclId":"arn:aws:wafv2:ap-northeast-1:XXXXXXXXXXXX:regional/webacl/kof-dev-web-acl/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","terminatingRuleId":"Default_Action","terminatingRuleType":"REGULAR","action":"ALLOW","terminatingRuleMatchDetails":[],"httpSourceName":"ALB","httpSourceId":"XXXXXXXXXXXX-app/test-dev-alb/XXXXXXXXXXXXXXXX","ruleGroupList":[],"rateBasedRuleList":[],"nonTerminatingMatchingRules":[],"httpRequest":{"clientIp":"XX.XX.XX.XX","country":"CN","headers":[{"name":"Host","value":"XX.XX.XX.XX"},{"name":"Content-Length","value":"19"},{"name":"Content-Type","value":"application/x-www-form-urlencoded"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0"},{"name":"Cache-Control","value":"no-cache"}],"uri":"/q.php","args":"","httpVersion":"HTTP/1.1","httpMethod":"POST","requestId":null}} {"timestamp":1591438092515,"formatVersion":1,"webaclId":"arn:aws:wafv2:ap-northeast-1:XXXXXXXXXXXX:regional/webacl/kof-dev-web-acl/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","terminatingRuleId":"Default_Action","terminatingRuleType":"REGULAR","action":"ALLOW","terminatingRuleMatchDetails":[],"httpSourceName":"ALB","httpSourceId":"XXXXXXXXXXXX-app/test-dev-alb/XXXXXXXXXXXXXXXX","ruleGroupList":[],"rateBasedRuleList":[],"nonTerminatingMatchingRules":[],"httpRequest":{"clientIp":"XX.XX.XX.XX","country":"CN","headers":[{"name":"Host","value":"XX.XX.XX.XX"},{"name":"Content-Length","value":"20"},{"name":"Content-Type","value":"application/x-www-form-urlencoded"},{"name":"User-Agent","value":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:61.0) Gecko/20100101 Firefox/61.0"},{"name":"Cache-Control","value":"no-cache"}],"uri":"/l7.php","args":"","httpVersion":"HTTP/1.1","httpMethod":"POST","requestId":null}} ・ 省略 ・ ・
さいごに
マネジメントコンソールでの構築については割愛していますが、ALB/WAF環境を構築する際の参考になれば幸いです。 S3にログが出力されていれば、AthenaやS3 Selectなどで解析することも可能ですので、以下も参考にしてください。